<?php

namespace App\Services;

use App\Exceptions\ApiException;
use App\Exceptions\ErrorCode;
use App\Models\AuthCode;
use App\Models\SocialAccount;
use App\Models\UpgradeInfo;
use App\Models\User;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\DB;

class AuthService extends Service
{
    protected $openPlatformService;
    protected $smsService;
    protected $socialAccountService;
    protected $userService;

    public function __construct(
        OpenPlatformService $openPlatformService,
        UserService $userService,
        SocialAccountService $socialAccountService,
        SmsService $smsService
    )
    {
        $this->openPlatformService = $openPlatformService;
        $this->smsService = $smsService;
        $this->socialAccountService = $socialAccountService;
        $this->userService = $userService;
    }

    /**
     * 微信绑定手机账号，这里有几个前提条件。
     * 1.微信信息已经被登记，因为微信登录第一时间会被记录。
     * 2.微信注册的账号没有绑定手机（phone 为 null），因为如果有绑手机就直接登录了。
     * 3.手机注册的账号如果有绑定微信，那一定不是参数传递的那个 openid 对应的微信。
     * 4.我暂时还没想到。
     *
     * @param $openid
     * @param $phone
     * @param $areaCode
     * @param $authCode
     * @return string $accessToken
     */
    public function changePhoneBinding(string $openid, string $phone, string $areaCode, string $authCode)
    {
        // 检查手机验证码。
        if (!$this->checkAuthCode($phone, $areaCode, $authCode)) {
            throw new ApiException('验证码无效！', ErrorCode::INVALID_AUTH_CODE);
        }

        // 查询微信绑定用户。
        $social_account = $this->socialAccountService->getByOpenId($openid);
        if (!$social_account) {
            // 理论上不会有此异常，除非人为手动删除数据。
            throw new ApiException('参数 openid 异常，找不到对应的社交账号信息。', ErrorCode::INVALID_PARAMATER);
        }

        // 查询手机是否注册。
        if ($phone_user = User::where(['phone' => $phone, 'area_code' => $areaCode])->first()) {
            // 检查手机是否被封禁。
            if ($phone_user->isBlock()) {
                throw new ApiException('该手机号已被封禁，如有疑问请联系客服！', ErrorCode::AUTH_USER_BLOCKED);
            }
            // 检查手机账号是否绑定其它微信。
            if ($phone_social_account = SocialAccount::where(['user_id' => $phone_user->id, 'type' => 0])->first()) {
                throw new ApiException('该手机已绑定其它微信账号。', ErrorCode::PHONE_ALREADY_BOUND_WECHAT);
            } else {
                if ($social_account->user_id != null) {
                    if ($phone_user->id === $social_account->user_id) { // 以下只是 API 级的防护，理论上 APP 端正常调用不会出现此情况。
                        return auth()->tokenById($phone_user->id);
                    } else {
                        if (!$social_user = $social_account->user) {
                            // 理论不会出现的情况，除非手机操作数据库并删除相关数据。
                            throw new ApiException('找不到社交账号绑定的用户！', ErrorCode::USER_NOT_FOUND);
                        }
                        $extra = [
                            'openid' => $social_account->openid,
                            'phone_user' => [
                                'id' => $phone_user->id,
                                'username' => $phone_user->username,
                                'avatar' => $phone_user->avatar,
                                'created_at' => $phone_user->created_at->getTimestamp(),
                                'is_login' => 0
                            ],
                            'social_user' => [
                                'id' => $social_account->openid,
                                'username' => $social_user->username,
                                'avatar' => $social_user->avatar,
                                'created_at' => $social_user->created_at->getTimestamp(),
                                'is_login' => 1
                            ]
                        ];
                        throw new ApiException('账号绑定冲突！', ErrorCode::ACCOUNT_CONFLICT, $extra);
                    }
                } else {
                    $social_account->update(['user_id' => $phone_user->id]);
                    return $phone_user;
                }
            }
        } else {
            if ($social_account->user_id != null) {
                // 更新微信注册账号的手机区号信息后返回 AccessToken。
                $social_user = User::find($social_account->user_id);
                $social_user->update(['phone' => $phone, 'area_code' => $areaCode]);
                return $social_user;
            } else {
                return DB::transaction(function () use ($social_account, $phone, $areaCode) {
                    // 创建账号。
                    $user = $this->userService->register(['openid' => $social_account->openid, 'phone' => $phone, 'area_code' => $areaCode]);
                    // 绑定账号。
                    $social_account->update(['user_id' => $user->id]);
                    return $user;
                });
            }
        }
    }

    public function changeWechatBinding($user, string $code)
    {
        $change_user = null;
        if ($user instanceof User) {
            $change_user = $user;
        } else if (is_int($user)) {
            $change_user = User::find($user);
        } else {
            throw new ApiException('参数异常，user 必须为 Integer 或 App\Models\User 类的实例！', ErrorCode::INVALID_PARAMATER);
        }
        $social_account = $this->getSocialAccountByCode($code);
        if (!$wechat_user = $social_account->user) {
            // 微信还没有绑定用户则直接绑定。
            $social_account->update(['user_id' => $change_user->id]);
            return $change_user;
        } else {
            $extra = [
                'openid' => $social_account->openid,
                'phone_user' => [
                    'id' => $change_user->id,
                    'username' => $change_user->username,
                    'avatar' => $change_user->avatar,
                    'created_at' => $change_user->created_at->getTimestamp(),
                    'is_login' => 1
                ],
                'social_user' => [
                    'id' => $social_account->openid,
                    'username' => $wechat_user->username,
                    'avatar' => $wechat_user->avatar,
                    'created_at' => $wechat_user->created_at->getTimestamp(),
                    'is_login' => 0
                ]
            ];
            throw new ApiException('账号绑定冲突！', ErrorCode::ACCOUNT_CONFLICT, $extra);
        }
    }

    public function checkAuthCode(string $phone, string $areaCode, string $authCode, bool $flash = true)
    {
        // 非生产环境的验证码永远为 888888。
        if (env('APP_ENV') !== 'production' && $authCode == '888888') {
            return true;
        }

        $auth_code = AuthCode::where([
            'phone' => $phone,
            'auth_code' => $authCode,
            'area_code' => $areaCode
        ])->active()->first();

        if (!$auth_code) {
            return false;
        }

        if ($flash) {
            $auth_code->expire();
        }

        return true;
    }

    public function checkForUpdate(int $build, string $platform)
    {
        $update_info = UpgradeInfo::where('platform', $platform)->first();
        if (!$update_info) {
            throw new ApiException('找不到平台 ' . $platform . ' 的更新信息！', ErrorCode::INVALID_PARAMATER);
        }
        return [
            'new_version' => $update_info->current_version,
            'new_build' => (int)$update_info->build,
            'update' => $update_info->build > $build,
            'force' => $update_info->force_build > $build,
            'update_url' => $update_info->upgrade_url ?? '',
            'update_description' => $update_info->update_description
        ];
    }

    public function getSocialAccountByCode(string $code)
    {
        if (env('APP_ENV') == 'local') {
            return SocialAccount::find(1); // 便于本地开发环境调试使用。
        }
        $this->openPlatformService->handleAuthorize($code);
        $wechat_authorizer = $this->openPlatformService->getAuthorizer();
        // 如果没有建立此账号信息，则先建立。
        if (!$social_account = $this->socialAccountService->getByOpenId($wechat_authorizer['openid'])) {
            // 转存头像。
            $wechat_authorizer['avatar'] = $this->transportAvatar($wechat_authorizer['headimgurl']);
            $social_account = $this->socialAccountService->createFromWechatAuthorizer($wechat_authorizer);
        }
        return $social_account;
    }

    public function loginByAuthCode(string $phone, string $areaCode, string $authCode)
    {
        if (!$user = $this->userService->getUserByPhone($phone, $areaCode)) {
            // 直接帮用户注册新账号。
            $user = $this->userService->register([
                'phone' => $phone,
                'area_code' => $areaCode,
                'auth_code' => $authCode
            ]);
        } else if (!$this->checkAuthCode($phone, $areaCode, $authCode)) {
            throw new ApiException('验证码无效！', ErrorCode::INVALID_AUTH_CODE);
        } else if ($user->isBlock()) {
            throw new ApiException('该用户已被封禁！', ErrorCode::AUTH_USER_BLOCKED);
        }
        return $user;
    }

    public function loginByPassword(string $phone, string $areaCode, string $password)
    {
        if (!$user = $this->userService->getUserByPhone($phone, $areaCode)) {
            throw new ApiException('此手机未注册！', ErrorCode::USER_NOT_FOUND);
        }
        if ($user->isBlock()) {
            throw new ApiException('该用户已被封禁！', ErrorCode::AUTH_USER_BLOCKED);
        }
        if (!auth()->attempt(['phone' => $phone, 'password' => $password])) {
            throw new ApiException('账号或密码错误！', ErrorCode::UNAUTHORIZED);
        }
        return $user;
    }

    public function sendAuthCode(string $phone, string $areaCode = '+86', int $expiredAt = 15)
    {
        $auth_code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_RIGHT);
        // 记录验证码。
        AuthCode::create([
            'area_code' => $areaCode,
            'phone' => $phone,
            'auth_code' => $auth_code,
            'expired_at' => Carbon::now()->addMinutes($expiredAt)
        ]);
        // 发送验证码（本地开发环境需要添加白名单，但 IP 不固定，所以不发送）。
        if (env('APP_ENV') == 'local') {
            return;
        }
        $this->smsService->send($areaCode . $phone, [
            'content' => '【礼物派】您的验证码是 ' . $auth_code,
        ]);
    }

    public function solvePhoneBindingConflict(string $openid, string $phone, string $areaCode)
    {
        // 检查参数是否合法。
        if (!$social_account = $this->socialAccountService->getByOpenId($openid)) {
            throw new ApiException('参数 openid 异常，找不到对应的社交账号信息。', ErrorCode::INVALID_PARAMATER);
        }
        if (!$wechat_user = $social_account->user) {
            throw new ApiException('参数 openid 异常，此微信未注册！', ErrorCode::INVALID_PARAMATER);
        }
        if ($wechat_user->phone != null) {
            throw new ApiException('参数 openid 异常，此微信已绑定手机！', ErrorCode::INVALID_PARAMATER);
        }
        if (!$phone_user = User::where(['phone' => $phone, 'area_code' => $areaCode])->first()) {
            throw new ApiException('参数 phone 异常，此手机未注册！', ErrorCode::INVALID_PARAMATER);
        }
        return DB::transaction(function () use ($wechat_user, $phone_user, $phone, $areaCode) {
            // 更新微信绑定用户的手机信息。
            $phone_user->update(['phone' => null, 'area_code' => null]);
            $wechat_user->update(['phone' => $phone, 'area_code' => $areaCode]);
            // 封禁手机绑定用户。
            $this->userService->block($phone_user);
            return $wechat_user;
        });
    }

    public function solveWechatBindingConflict($user, $openid)
    {
        if (is_int($user)) {
            $user = User::find($user);
        } else if (!$user instanceof User) {
            throw new ApiException('参数异常，user 必须为 Integer 或 App\Models\User 类的实例！', ErrorCode::INVALID_PARAMATER);
        }
        if (!$social_account = SocialAccount::where(['openid' => $openid, 'type' => 0])->first()) {
            throw new ApiException('参数 openid 异常，找不到对应的社交账号信息。', ErrorCode::INVALID_PARAMATER);
        }
        return DB::transaction(function () use ($user, $social_account) {
            if ($change_user_social_account = SocialAccount::where(['user_id' => $user->id, 'type' => 0])->first()) {
                $change_user_social_account->update(['user_id' => null]);
            }
            $wechat_user = $social_account->user;
            $social_account->update(['user_id' => $user->id]);
            if ($wechat_user) {
                $this->userService->block($wechat_user);
            }
            return $user;
        });
    }

    protected function transportAvatar($remoteAvatarUrl)
    {
        // 根据微信和 QQ 开放平台的 API 规则，在头像链接后接 /0 为高清分辨率头像地址。
        $remoteAvatarUrl = substr($remoteAvatarUrl, 0, strrpos($remoteAvatarUrl, '/')) . '/0';
        $http_client = new Client();
        $response = $http_client->get($remoteAvatarUrl);
        $data = $response->getBody()->getContents();
        $content_type = $response->getHeader('content-type');
        if (is_array($content_type)) {
            $content_type = $content_type[0];
        }
        $key = 'avatars/' . md5($data);
        $oss_client = app('aliyun-oss');
        $result = $oss_client->putObject(env('OSS_BUCKET'), $key, $data, ['Content-Type' => $content_type]);
        return str_replace('http://', 'https://', $result['oss-request-url']);
    }

    public function wechatAuthorize($authorizeCode)
    {
        $social_account = $this->getSocialAccountByCode($authorizeCode);
        $user = User::find($social_account->user_id);
        if (!$user || $user->phone == null) {
            // 返回没有绑定手机的异常。
            throw new ApiException('此社交账号未绑定手机', ErrorCode::SOCIAL_ACCOUNT_NOT_BOUND_PHONE, ['openid' => $social_account->openid]);
        } else if ($user->isBlock()) {
            throw new ApiException('该用户已被封禁！', ErrorCode::AUTH_USER_BLOCKED);
        } else {
            return $user;
        }
    }
}